package nz.ac.massey.jbuif.utils;

import java.beans.Introspector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;


/**
 * utils class
 * @author ahkiouma
 *
 */
public class Utils {

	private static String CLASS_REGEX = ".*\\.(class|Class|CLASS)$";
	private static String BEAN_INFO_REGEX = ".*BeanInfo\\.(class|Class|CLASS)$";

	
	/**
	 * create a bean from input name
	 * @param className name of the class
	 * @param loader ClassLoader
	 * @return object instance of className
	 */
	@SuppressWarnings("unchecked")
	public static Object createBeanFromName(String className, ClassLoader loader) {
		try {
			Class klazz = Class.forName(className, true, loader);
			Object instance = klazz.newInstance();
			return instance;

		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			JOptionPane.showMessageDialog(null,
					"Could not create an instance of class " + className,
					"Class Not Found", 0);

			System.err.println("Error: Could not find class " + className
					+ ". Please ensure it has been loaded.");
			// TODO display a message box and ask for the user to give the jar,
			// or remove the bean from the available bean list
			e.printStackTrace();
		}

		return null;
	}

	
	/**
	 * load a jar and get the object collections
	 * @param file input file
	 * @param jcl instance of JarClassLoader
	 * @return collection of strings with in the file
	 * @throws MalformedURLException
	 */
	public static Collection<String> loadJar(File file, JarClassLoader jcl)
	throws MalformedURLException {

		URL fileURL = JarClassLoader.getURLForFile(file);

		jcl.addJar(fileURL);

		JarFile jarFile = null;
		@SuppressWarnings("unused")
		String[] classes = null;
		Vector<String> dirs = new Vector<String>();
		Vector<String> beanClassesInJar = null;

		try {
			jarFile = new JarFile(file);

			beanClassesInJar = new Vector<String>();

			Enumeration<JarEntry> entries = jarFile.entries();
			while (entries.hasMoreElements()) {
				JarEntry jEntry = entries.nextElement();
				String name = jEntry.getName();

				if (name.matches(CLASS_REGEX)) {
					// convert all '/' to '.', and remove the .class suffix.
					String className = name.replace('/', '.').substring(0,
							name.length() - 6);

					if (name.matches(BEAN_INFO_REGEX))
						// add the directory to the Introspector search path.
					{
						int index = className.lastIndexOf('.');
						className = className.substring(0, index);
						if (!dirs.contains(className)) {
							dirs.add(className);
						}
					} else
						// add the class to the list of usable classes in the jar,
					{
						// TODO: need to handle nested classes?
						beanClassesInJar.add(className);
					}
				}
			}

			String[] introspectorPath = new String[dirs.size()];
			dirs.toArray(introspectorPath);

			Introspector.setBeanInfoSearchPath(introspectorPath);

		} catch (IOException e) {
			e.printStackTrace();
		}

		return beanClassesInJar;
	}

	
	/**
	 * load a jar and get the object collections
	 * @param fileName string input file
	 * @param jcl JarClassLoader
	 * @return collection of strings with in the jar
	 * @throws MalformedURLException
	 */
	public static Collection<String> loadJar(String fileName, JarClassLoader jcl)
	throws MalformedURLException {
		File file = new File(fileName);

		URL urls[] = new URL[ ]{ file.toURI().toURL() };
		ClassLoader aCL = Thread.currentThread().getContextClassLoader();
		URLClassLoader aUrlCL = new URLClassLoader(urls, aCL);

		Thread.currentThread().setContextClassLoader(aUrlCL);
		
//		System.out.println ("Added to build path");
		
		return loadJar(file, jcl);
	}


	/**
	 * cloneObject clone an bean object
	 * @param bean object bean - object must be serializable
	 * @return cloned object
	 */
	public static Object cloneObject(Object bean){
		try
		{
			ByteArrayOutputStream out = new ByteArrayOutputStream();
		    ObjectOutputStream oout = new ObjectOutputStream(out);
		    oout.writeObject(bean);
		    
		    ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
		    return in.readObject();
		}
		catch (Exception exception) {
			System.err.println (exception.toString());
		}
		return null;
	}
	
	
	/**
	 * control the image icons
	 * @param resourcePath string resource path
	 * @return an icon from path
	 */
	private static ImageIcon getIconFromResource(String resourcePath)
	{
		resourcePath = "../"+resourcePath;
		URL imageURL = Utils.class.getResource(resourcePath);
		ImageIcon iIcon = null;
		if(imageURL != null){
			iIcon = new ImageIcon( imageURL );
		}
		else
		{
			System.err.println("No url for "+resourcePath);
		}
		return iIcon;
	}
	
	
	/**
	 * controls the menu icons
	 * @param jmi menu item
	 * @param iconPath string icon path
	 */
	public static void setJMenuItemIcon(JMenuItem jmi, String iconPath)
	{
		ImageIcon ic = getIconFromResource(iconPath);
		if(ic != null)
		{
			jmi.setIcon(ic);
		}
	}
	
	
	/**
	 * controls the buttons icons
	 * @param jb a button
	 * @param iconPath iconPath string icon path
	 */
	public static void setJButtonIcon(JButton jb, String iconPath)
	{
		ImageIcon ic = getIconFromResource(iconPath);
		if(ic != null)
		{
			jb.setIcon(ic);
		}
	}
}
